<?php

namespace App\Services\Product;

use App\Events\DeviceInfoUpdate;
use App\Events\UserCommand;
use App\Exceptions\DeviceException;
use App\Exceptions\UserException;
use App\Facades\ChargeRecordManager;
use App\Facades\UserManager;
use App\Facades\DeviceManager;
use App\Facades\WeChatService;
use App\Jobs\CommandRepeater;
use App\Jobs\SendTemplateMessage;
use App\Models\Device;
use App\Models\DeviceRecord;
use App\Models\UserInfo;
use App\Utils\DeviceServer;
use Cache;
use Carbon\Carbon;
use DB;
use Exception;
use Illuminate\Foundation\Bus\DispatchesJobs;

class DeviceService
{
	use DispatchesJobs;

	/**
	 * 绑定
	 * @param  string $deviceCode 设备编码
	 */
	public function bind($deviceCode)
	{
		try{
			DB::transaction(function() use ($deviceCode) {
			    $user = UserManager::getUser();
			    $device = DeviceManager::isCreateDeviceRecord($deviceCode);
			    UserManager::updateUserInfo($user->id, ['current_device_id' => $device->id]);
			    Cache::forget($user->userInfo->open_id);
			});
		}catch(Exception $e){
			throw new DeviceException('bind');
		}
	}

	/**
	 * 根据设备号解绑设备
	 * @param  string $deviceCode 设备编码
	 */
	public function unbindByDeviceCode($deviceCode = null)
	{
		try{
			return DB::transaction(function() use ($deviceCode) {
				$userInfo = UserManager::getUser()->userInfo;
				UserManager::updateUserInfo($userInfo->user_id, ['current_device_id' => null]);
				Cache::forget('userInfo'.$userInfo->user_id);
			});
		}catch(Exception $e){
			throw new DeviceException('unbind');
		}
	}

	/**
	 * 获取设备状态
	 * @return array 	设备状态
	 */
	public function getState($throwException = true)
	{
		$user = UserManager::getUser();
		$userInfo = $user->userInfo->load(['device' => function($query){
			$query->select('id', 'code');
		}]);
		if(is_null($userInfo->device))
		{
			throw new DeviceException('noBind');
		}
		$deviceCode = $userInfo->device->code;
		$state = Cache::get($deviceCode.'state');
		if($throwException){
			$this->isOffline($deviceCode);
		}

		if(count($state))
		{
			return $state;
		}
	}

	/**
	 * 发送控制命令
	 * @param  string $command 命令
	 */
	public function control($command, $num, $deviceCode = null, $orderId = null, $userId = null, $throw = true)
	{
		if(is_null($deviceCode)){
			if(is_null($userId)){
				$userId = UserManager::getUser()->id;
			}
			$userInfo = Cache::remember('userInfo'.$userId, env('CACHE_USER_INFO_TIME', 1), function() use ($userId) {
				return UserManager::getUserById($userId)->userInfo->load(['device' => function($query){
					$query->select('id', 'code');
				}]);
			});
			$deviceCode = $userInfo->device->code;
		}

		$state = Cache::get($deviceCode.'state');
		
		if(!$this->isOffline($deviceCode, $throw)){

			if(!in_array($command, ['halt', 'stop_halt', 'recharge']) && $state['decStatus'] == 7 && $command != 'power_on'){
				throw new DeviceException('powerOff');
			}
			// 某些特定状态下的特定操作无需执行
			if(in_array($command, ['power_on', 'power_off', 'wash', 'wash_cancel'])) {
				$state = Cache::get($deviceCode.'state');
				if(in_array($state['decStatus'], [2, 4, 6, 8, 9])) {
					return ;
				}
			}
			event(new UserCommand($command, $num, $deviceCode, $orderId));
		}

		//停机和充值的重发队列
		if(in_array($command, ['halt', 'stop_halt', 'recharge', 'reduce_flow'])){
			$job = (new CommandRepeater($command, $num, $deviceCode, $orderId, $userId))->delay(20)->onQueue('commandRepeater');
			$this->dispatch($job);
		}
	}

	/**
	 * 获取控制指令 byte
	 * @param  string  $command   
	 * @param  string  $num       
	 * @param  string  $deviceCode    
	 * @param  integer $orderId 充值记录 id
	 * @return              
	 */
	public function getCmd($command, $num, $deviceCode, $orderId = 0)
	{
		$binDeviceCode = Cache::get($deviceCode.'BinDeviceCode');
		switch ($command)
		{
			case 'power_on':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 2, 1);
				break;
			case 'power_off':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 2, 0);
				break;
			case 'unlock':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 4, 1);
				break;
			case 'wash':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 1, 1);
				break;
			case 'wash_cancel':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 1, 0);
				break;
			case 'reset_filter':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 17, 0x07, 3, $num);
				break;
			case 'halt':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 16, 0x0a, 1);
				DeviceManager::updateDevice($deviceCode, ['status' => Device::STATUS_HALTING], 1);
				break;
			case 'stop_halt':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 16, 0x0a, 0);
				DeviceManager::updateDevice($deviceCode, ['status' => Device::STATUS_HALTING], 0);
				break;
			case 'recharge':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 19, 0x08, $num, 0, $orderId, 0x2b000000);
                break;
            case 'reduce_flow':
				$cmd = DeviceServer::getCmdByte($binDeviceCode, 19, 0x08, $num, 0, $orderId, 0x2d000000);
                break;
			default:
				throw new DeviceException('command');
				break;
		}
		return $cmd;
	}

	/**
	 * 获取设备列表
	 * @param   $condition
	 * @param   $status
	 * @return
	 */
	public function findDeviceRecordLlist($condition, $status)
	{
		$resultSet = DeviceManager::findDeviceRecordWithCodeByStatus($status, $condition);
		return $resultSet->orderBy('TDS2', 'desc')->paginate(15);
	}

	/**
	 * 根据设备码获取设备信息
	 * @param   $deviceCode 
	 * @return              
	 */
	public function findDeviceRecordListByDeviceCode($deviceId)
	{
		$deviceRecords = DeviceManager::getAllDeviceRecordWithCodeByDeviceCode($deviceId)
			->with(['device' => function($query){
				$query->with(['installInfo' => function($query){
					$query->select(['device_code', 'customer_name', 'phone', 'installation_address', 'dealer']);
				}])->select(['id', 'code']);
		}])->latest()->paginate(15);
		return $deviceRecords;
	}

	public function getHreatBeatData(){
		return pack("v5C3V2v", 0x00bb, 0x0006, 0x0000, 0x000D, 0x0002, 0x12, 0x00, 0x00, 0X00000000, 0X00000000, 0x4FE5);
	}

	/**
	 * 处理设备数据
	 * @return  
	 */
	public function handle($dataCmd, $fd, $data)
	{
	    $formatData = DeviceServer::dataFormat($dataCmd, $data);
	    
	    //缓存设备数据
	    $formatData = $this->cacheDeviceData($formatData, $fd, $data, $dataCmd);
	    // logger(['code' => $formatData['deviceCode']]);

	    event(new DeviceInfoUpdate($formatData));

	    DeviceManager::isCreateDeviceRecord($formatData['deviceCode']);
	    
	    // $this->sendInformMessage($formatData);
	    
	    $this->isSaveDeviceData($formatData);
	}

	/**
	 * 缓存设备相关信息
	 * @param   $formatData 
	 * @param   $fd         
	 * @param   $data       
	 * @return              
	 */
	public function cacheDeviceData($formatData, $fd, $data, $dataCmd)
	{
		$expiresAt = Carbon::now()->addMinutes(env('CACHE_DEVICE_DATA'));
		$formatDataCache = Cache::remember($formatData['deviceCode'].'state', $expiresAt, function() use ($formatData) {
			return $formatData = [
				'deviceCode' => $formatData['deviceCode'],
				'messageNumber' => 0,
				'decStatus' => 0,
				'decStatusDescription' => '离线',
				'TDS1' => '000',
				'TDS2' => '000',
				'filterCnt1' => 0,
				'filterCnt2' => 0,
				'filterCnt3' => 0,
				'filterCnt4' => 0,
				'filterCnt5' => 0,
				'filterSet1' => 1,
				'filterSet2' => 1,
				'filterSet3' => 1,
				'filterSet4' => 1,
				'filterSet5' => 1,
				'filter1' => 0,
				'filter2' => 0,
				'filter3' => 0,
				'filter4' => 0,
				'filter5' => 0,
				'totalFlow' => 0,
				'residualFlow' => 0,
				'cmd' => [],
			];
		});
		$keys = [
			'messageNumber',
			'decStatus',
			'decStatusDescription',
			'TDS1',
			'TDS2',
			'filterCnt1',
			'filterCnt2',
			'filterCnt3',
			'filterCnt4',
			'filterCnt5',
			'filterSet1',
			'filterSet2',
			'filterSet3',
			'filterSet4',
			'filterSet5',
			'filter1',
			'filter2',
			'filter3',
			'filter4',
			'filter5',
			'totalFlow',
			'residualFlow',
		];
		foreach($keys as $key){
			if(array_key_exists($key, $formatData)){
				$formatDataCache[$key] = $formatData[$key];
			}
		}
		//缓存设备连接 fd
		Cache::put($formatData['deviceCode'].'fd', $fd, $expiresAt);
		logger([$formatData['deviceCode'] => $fd]);
		if(!in_array($dataCmd, $formatDataCache['cmd'])){
			$formatDataCache['cmd'][] = $dataCmd;
		}
		//缓存设备数据
		Cache::put($formatData['deviceCode'].'state', $formatDataCache, $expiresAt);
	    
		//缓存设备码二进制数据
		Cache::put($formatData['deviceCode'].'BinDeviceCode', substr($data, 9, 8), $expiresAt);
		return $formatDataCache;
	}

	/**
	 * 发送模板消息
	 * @param   $formatData 
	 * @return              
	 */
	// public function sendInformMessage($formatData)
	// {
	// 	if($formatData['filter1'] <= Device::REMIND_LIMIT || $formatData['filter2'] <= Device::REMIND_LIMIT)
	// 	{
	// 		$this->dispatch(new SendTemplateMessage($formatData));
	// 	}
	// }

	/**
	 * 存储记录
	 * @param   $formatData 
	 * @return              
	 */
	public function isSaveDeviceData($formatData)
	{
		if(count(array_diff([1, 2, 3, 4], $formatData['cmd'])) == 0){
			if(!Cache::has($formatData['deviceCode'].'record')){
				$this->saveDeviceData($formatData);
				Cache::put($formatData['deviceCode'].'record', 1, Carbon::now()->addMinutes(60*24));
			}
			if($formatData['decStatus'] == DeviceRecord::HYDROPENIA && !Cache::has($formatData['deviceCode'].'recordHydropenia')){
				$this->saveDeviceData($formatData);
				Cache::put($formatData['deviceCode'].'recordHydropenia', 1, Carbon::now()->addMinutes(60*24));
			}
			if($formatData['decStatus'] == DeviceRecord::OVERHAUL && !Cache::has($formatData['deviceCode'].'recordOverhaul')){
				$this->saveDeviceData($formatData);
				Cache::put($formatData['deviceCode'].'recordOverhaul', 1, Carbon::now()->addMinutes(60*24));
			}
			if($formatData['decStatus'] == DeviceRecord::HALT && !Cache::has($formatData['deviceCode'].'halt')){
				$this->saveDeviceData($formatData);
				Cache::put($formatData['deviceCode'].'halt', 1, Carbon::now()->addMinutes(60*24));
			}
		}
	}

	/**
	 * 保存设备记录数据
	 * @param  [type] $formatData 
	 * @return [type]             
	 */
	public function saveDeviceData($formatData)
	{
		$createData = [
			'device_code' => $formatData['deviceCode'],
			'TDS1' => $formatData['TDS1'],
			'TDS2' => $formatData['TDS2'],
			'message_number' => $formatData['messageNumber'],
			'dec_status' => $formatData['decStatus'],
			'filter_cnt1' => $formatData['filterCnt1'],
			'filter_cnt2' => $formatData['filterCnt2'],
			'filter_cnt3' => $formatData['filterCnt3'],
			'filter_cnt4' => $formatData['filterCnt4'],
			'filter_cnt5' => $formatData['filterCnt5'],
			'filter_set1' => $formatData['filterSet1'],
			'filter_set2' => $formatData['filterSet2'],
			'filter_set3' => $formatData['filterSet3'],
			'filter_set4' => $formatData['filterSet4'],
			'filter_set5' => $formatData['filterSet5'],
			'filter1' => $formatData['filter1'],
			'filter2' => $formatData['filter2'],
			'filter3' => $formatData['filter3'],
			'filter4' => $formatData['filter4'],
			'filter5' => $formatData['filter5'],
			'total_flow' => $formatData['totalFlow'],
			'residual_flow' => $formatData['residualFlow'],
		];
		DeviceManager::save($createData);
	}

	public function sendHeartBeatData($fd)
	{
		event(new UserCommand('heart_beat_data', null, $fd));
	}

	/**
	 * 根据用户获取设备码
	 * @return [type] 
	 */
	public function getDeviceCodeByUser()
	{
		$userInfo = UserManager::getUser()->userInfo;
		$userInfo->load(['device' => function($query){
			$query->select('id', 'code');
		}]);
		return $userInfo->device->code;
	}

	/**
	 * 设备是否离线
	 * @param  string  $deviceCode 
	 * @param  boolean $throw      
	 * @return boolean             
	 */
	public function isOffline($deviceCode, $throw = true)
	{
		$fd = Cache::get($deviceCode.'fd');
		$fds = Cache::get('fds');
		if($throw){
			if(is_null($fd) || is_null($fds) || !in_array($fd, $fds)){
				throw new DeviceException('notConnect');
			}
		}else{
			return (is_null($fd) || is_null($fds) || !in_array($fd, $fds));
		}
	}

	/**
	 * 更新设备记录数据
	 * @param  integer $id   
	 * @param  array $data 
	 * @return        
	 */
	public function update($id, $data)
	{
		$deviceRecord = DeviceManager::getDeviceRecordById($id);
		$deviceRecord->update($data);
		return $deviceRecord;
	}

	/**
	 * 获取设备提醒列表
	 * @param  string  $condition 
	 * @return              
	 */
	public function findFlowRemindList($condition)
	{
		$listBuilder = DeviceManager::getDeviceRemindList($condition);
		$listBuilder->where('residual_flow', '<=', 100);
		return $listBuilder->orderBy('flow_status')->orderBy('residual_flow', 'desc')->latest()->select([
																				    'id',
																				    'device_id',
																				    'residual_flow',
																				    'created_at',
																				    'flow_status',
																				    ])->paginate(15);
	}

	/**
	 * 获取设备提醒列表
	 * @param  string  $condition 
	 * @return              
	 */
	public function findFliterRemindList($condition)
	{
		$listBuilder = DeviceManager::getDeviceRemindList($condition);
		$listBuilder->where(function($query){
			for($i=1; $i<=5; $i++){
		    	$query->orWhere('filter'.$i, '<=', 15);
			}
		});
		return $listBuilder->orderBy('filter_status')
						   ->orderBy('filter1')
						   ->orderBy('filter2')
						   ->orderBy('filter3')
						   ->orderBy('filter4')
						   ->orderBy('filter5')
						   ->latest()
						   ->select([
				   					'id',
				   					'device_id',
				   					'filter1',
				   					'filter2',
				   					'filter3',
				   					'filter4',
				   					'filter5',
				   					'created_at',
				   					'filter_status',
										])
							->paginate(15);
		
	}

	/**
	 * 根据状态对设备提醒记录计数
	 * @param integer  $status 
	 * @return          
	 */
	public function countFlowRemindListByStatus($status)
	{
		return DeviceManager::countFlowRemindListByStatus($status);
	}

	/**
	 * 根据状态对设备提醒记录计数
	 * @param integer  $status 
	 * @return          
	 */
	public function countFilterRemindListByStatus($status)
	{
		return DeviceManager::countFilterRemindListByStatus($status);
	}

	/**
	 * 根据状态对维护信息记录计数
	 * @param integer  $status 
	 * @return          
	 */
	public function countmaintainInfoListByStatus($status)
	{
		return DeviceManager::getMaintainInfoListBuilderOrderByCreatedAt()->where('status', $status)->count();
	}

	public function getInstallAddressByDeviceCode($deviceCode)
	{
		return DeviceManager::getInstallInfoByQueryColumn('device_code', $deviceCode)->installation_address;
	}

	

}